// let canvas, ctx, w, h, laser, text, particles, input;
const canvas = document.getElementById("canvas"),
  input = document.getElementById("input"),
  ctx = canvas.getContext("2d"),
  w = window.innerWidth,
  h = window.innerHeight,
  laser = [],
  particles = [];

canvas.width = w;
canvas.height = h;

let text;

class Laser {
  constructor(options) {
    options = options || {};
    this.lifespan = options.lifespan || Math.round(Math.random() * 20 + 20);
    this.maxlife = this.lifespan;
    this.color = options.color || "#fd2423";
    this.x = options.x || Math.random() * w;
    this.y = options.y || Math.random() * h;
    this.width = options.width || 2;

    this.update = function (index, array) {
      this.lifespan > 0 && this.lifespan--;
      this.lifespan <= 0 && this.remove(index, array);
    };

    this.render = function (ctx) {
      if (this.lifespan <= 0) return;
      ctx.beginPath();
      ctx.globalAlpha = this.lifespan / this.maxlife;
      ctx.strokeStyle = this.color;
      ctx.lineWidth = this.width;
      ctx.moveTo(this.x, this.y);
      ctx.lineTo(w, this.y);
      ctx.stroke();
      ctx.closePath();
    };

    this.remove = function (index, array) {
      array.splice(index, 1);
    };
  }
}

class Spark {
  constructor(options) {
    options = options || {};
    this.x = options.x || w * 0.5;
    this.y = options.y || h * 0.5;
    this.v = options.v || {
      direct: Math.random() * Math.PI * 2,
      weight: Math.random() * 10 + 2,
      friction: 0.94,
    };
    this.a = options.a || {
      change: Math.random() * 0.2 - 0.1,
      min: this.v.direct - Math.PI * 0.4,
      max: this.v.direct + Math.PI * 0.4,
    };
    this.g = options.g || {
      direct: Math.PI * 0.5 + (Math.random() * 0.4 - 0.2),
      weight: Math.random() * 0.5 + 0.5,
    };
    this.width = options.width || Math.random() * 3;
    this.lifespan = options.lifespan || Math.round(Math.random() * 20 + 40);
    this.maxlife = this.lifespan;
    this.color = options.color || "#fdab23";
    this.prev = { x: this.x, y: this.y };

    this.update = function (index, array) {
      this.prev = { x: this.x, y: this.y };
      this.x += Math.cos(this.v.direct) * this.v.weight;
      this.x += Math.cos(this.g.direct) * this.g.weight;
      this.y += Math.sin(this.v.direct) * this.v.weight;
      this.y += Math.sin(this.g.direct) * this.g.weight;
      this.v.weight *= this.v.friction;
      this.v.direct += this.a.change;
      (this.v.direct > this.a.max || this.v.direct < this.a.min) &&
        (this.a.change *= -1);
      this.lifespan > 0 && this.lifespan--;
      this.lifespan <= 0 && this.remove(index, array);
    };

    this.render = function (ctx) {
      if (this.lifespan <= 0) return;
      ctx.beginPath();
      ctx.globalAlpha = this.lifespan / this.maxlife;
      ctx.strokeStyle = this.color;
      ctx.lineWidth = this.width;
      ctx.moveTo(this.x, this.y);
      ctx.lineTo(this.prev.x, this.prev.y);
      ctx.stroke();
      ctx.closePath();
    };

    this.remove = function (index, array) {
      array.splice(index, 1);
    };
  }
}

class Particles {
  constructor(options) {
    options = options || {};
    this.max = options.max || Math.round(Math.random() * 20 + 10);
    this.sparks = [...new Array(this.max)].map(() => new Spark(options));

    this.update = function () {
      this.sparks.forEach((s, i) => s.update(i, this.sparks));
    };

    this.render = function (ctx) {
      this.sparks.forEach((s) => s.render(ctx));
    };
  }
}

class Text {
  constructor(options) {
    options = options || {};
    const pool = document.createElement("canvas");
    const buffer = pool.getContext("2d");
    pool.width = w;
    buffer.fillStyle = "#000000";
    buffer.fillRect(0, 0, pool.width, pool.height);

    this.size = options.size || 100;
    this.copy = (options.copy || `Hello!`) + " ";
    this.color = options.color || "#fecd96";
    this.delay = options.delay || 4;
    this.basedelay = this.delay;
    buffer.font = `${this.size}px Comic Sans MS`;
    this.bound = buffer.measureText(this.copy);
    this.bound.height = this.size * 1.5;
    this.x = options.x || w * 0.5 - this.bound.width * 0.5;
    this.y = options.y || h * 0.5 - this.size * 0.5;

    buffer.strokeStyle = this.color;
    buffer.strokeText(this.copy, 0, this.bound.height * 0.8);
    this.data = buffer.getImageData(0, 0, this.bound.width, this.bound.height);
    this.index = 0;

    this.update = function () {
      if (this.index >= this.bound.width) {
        this.index = 0;
        return;
      }
      const data = this.data.data;
      for (let i = this.index * 4; i < data.length; i += 4 * this.data.width) {
        const bitmap = data[i] + data[i + 1] + data[i + 2] + data[i + 3];
        if (bitmap > 255 && Math.random() > 0.86) {
          const x = this.x + this.index;
          const y = this.y + i / this.bound.width / 4;
          laser.push(
            new Laser({
              x: x,
              y: y,
            })
          );
          Math.random() > 0.7 &&
            particles.push(
              new Particles({
                x: x,
                y: y,
              })
            );
        }
      }
      this.delay-- < 0 && this.index++ && (this.delay += this.basedelay);
    };

    this.render = function (ctx) {
      ctx.putImageData(
        this.data,
        this.x,
        this.y,
        0,
        0,
        this.index,
        this.bound.height
      );
    };
  }
}

function loop() {
  update();
  render();
  requestAnimationFrame(loop);
}

function update() {
  text.update();
  laser.forEach((l, i) => l.update(i, laser));
  particles.forEach((p) => p.update());
}

function render() {
  ctx.globalCompositeOperation = "source-over";
  ctx.globalAlpha = 0.9;
  ctx.fillStyle = "#000000";
  ctx.fillRect(0, 0, w, h);
  //
  ctx.globalCompositeOperation = "screen";
  text.render(ctx);
  laser.forEach((l) => l.render(ctx));
  particles.forEach((p) => p.render(ctx));
}

(function () {
  //
  text = new Text({
    copy: "胡章清",
  });

  canvas.addEventListener("click", (e) => {
    const x = e.clientX;
    const y = e.clientY;
    laser.push(
      new Laser({
        x: x,
        y: y,
      })
    );
    particles.push(
      new Particles({
        x: x,
        y: y,
      })
    );
  });
  //   let cb = 0;
  //   input.addEventListener("keyup", (e) => {
  //     clearTimeout(cb);
  //     cb = setTimeout(() => {
  //       text = new Text({
  //         copy: input.value,
  //       });
  //     }, 300);
  //   });
  //
  loop();
})();
